<template>
  <view class="canvas-box" :style="`width:${rpx2px(width)}px;height:${rpx2px(height)}px;`">
    <canvas
      :canvas-id="canvasId"
      :style="`width:${rpx2px(width)}px;height:${rpx2px(height)}px;`"
      class="canvas-box-canvas"
    ></canvas>
    <image :src="imgPath" class="canvas-img" />
  </view>
</template>
<script>
/**
	 * canvasId   canvas-id的名称
	 * auto		  自动渲染[设置成false需要手动redraw]
	 * backgroundColor canvas的背景颜色
	 * width 整个canvas的宽
	 * height 整个canvas的高
	 * //整个vanvas描述
	 // elementList:{
	 // 	images:[//图片对象
	 // 		{
	 // 			path:"http://t6.xin.cn/uploads/20231113/a07068b23e1d4969d09f4105fee78631.png",//图片路径[必填]注意h5不能跨域下载图片，需处理跨域问题
	 // 			dx:0,//左上角x轴[必填]
	 // 			dy:0,//左上角y轴[必填]
	 // 			dWidth:'',//宽度[必填]
	 // 			dHeight:'',//高度[必填]
	 // 			sx:650/2,//绘画的宽度[选填]
	 // 			sy:860/2,//绘画的高度[选填]
	 // 		}
	 // 	],
	 // 	text:[//渲染文字
	 // 		{
	 // 			value:"邀请码：",//渲染的文字
	 // 			color:"#999999",//文字颜色[选填]
	 // 			size:"14",//大小[选填]
	 // 			font:"PingFang SC-Regular, PingFang SC;",//字体[选填]
	 // 			x:"13",//左上角x轴[必填]
	 // 			y:"460",//左上角y轴[必填]
	 // 			backgroundColor:"#ffffff"//背景色
	 // 			maxWidth:"",//字体最大宽度
	 // 			maxHeight:"",//字体最大高度
	 // 		},
	 // 	],
	 // 	qr:[//渲染的二维码
	 // 		{
	 // 			url:"http://t6.xin.cn/#/pages/user/code_login?invite_mobile=5",//二维码文字[必填]
	 // 			dx:241,//左上角x轴[必填]
	 // 			dy:452,//左上角y轴[必填]
	 // 			size:71,//二维码大小
	 // 			color:"#333333",//二维码前景色
	 // 			backgroundColor:"#ffffff"//二维码背景色
	 // 		}
	 // 	]

	 // }
	 */
import UQRCode from './uqrcode'
export default {
  name: 'MCanvas',
  data() {
    return {
      canvasContext: null,
      has: true,
      imgPath: '',
      screenWidth: 750,
    }
  },
  props: {
    canvasId: {
      type: String,
      default: 'mu-canvas-id',
    },
    elementList: {
      type: Object,
      default: () => {},
    },
    auto: {
      type: Boolean,
      default: true,
    },
    background: {
      type: [String, Object],
      default: '#ffffff',
    },
    width: {
      type: Number,
      default: 200,
    },
    height: {
      type: Number,
      default: 200,
    },
  },
  watch: {
    elementList: {
      handler(newName, oldName) {
        if (this.auto === true) {
          this.redraw()
        }
      },
    },
  },
  created() {
    this.screenWidth = uni.getSystemInfoSync().screenWidth
    setTimeout(() => {
      if (this.auto === true) {
        this.redraw()
      }
    }, 300)
  },
  methods: {
    // 开始绘制
    async redraw(callback) {
      console.log('开始绘制')
      uni.showLoading({
        mask: true,
        title: '海报生成中…',
      })
      // 获取绘画对象
      let canvasContext = this.canvasContext
      if (canvasContext == null) {
        canvasContext = uni.createCanvasContext(this.canvasId, this)
        this.canvasContext = canvasContext
      }

      // 绘制背景
      if (typeof this.background === 'string') {
        canvasContext.setFillStyle(this.background)
        canvasContext.fillRect(0, 0, this.rpx2px(this.width), this.rpx2px(this.height))
      } else {
        this.drawRoundedRect(
          canvasContext,
          0,
          0,
          this.rpx2px(this.width),
          this.rpx2px(this.height),
          this.background,
        )
      }

      // 绘画文字
      if (this.elementList.text && this.elementList.text.length > 0) {
        const texts = this.elementList.text
        // console.log(texts);
        for (const textindex in texts) {
          await this._drawTxt(canvasContext, texts[textindex])
        }
      }

      // 绘画二维码
      if (this.elementList.qr && this.elementList.qr.length > 0) {
        const qrs = this.elementList.qr
        for (const qrindex in qrs) {
          await this._drowQr(canvasContext, qrs[qrindex])
        }
      }

      // 绘画图片
      if (this.elementList.images && this.elementList.images.length > 0) {
        const images = this.elementList.images
        for (const key in images) {
          await this._drawImges(canvasContext, images[key])
        }
      }

      canvasContext.draw(false, async (e) => {
        // console.log(e);
        console.log('绘制结束')
        this.imgPath = await this.getImgSrc()
        uni.hideLoading({})
        if (callback) {
          callback(e)
        }
      })
    },
    drawRoundedRect(ctx, x, y, width, height, background) {
      const { color } = background
      const radius = this.rpx2px(background.radius)
      const holeTop = this.rpx2px(background.holeTop)

      const left = x
      const right = x + width
      const top = y
      const bottom = y + height

      ctx.beginPath()
      ctx.moveTo(left + radius, top)
      // 第一个圆角
      ctx.lineTo(right - radius, top)
      ctx.arcTo(right, top, right, top + radius, radius)
      // 第二个圆角
      ctx.lineTo(right, bottom - radius)
      ctx.arcTo(right, bottom, right - radius, bottom, radius)
      // 第三个圆角
      ctx.lineTo(left + radius, bottom)
      ctx.arcTo(left, bottom, left, bottom - radius, radius)
      // 第四个圆角
      ctx.lineTo(left, top + radius)
      ctx.arcTo(left, top, left + radius, top, radius)

      ctx.setFillStyle(color)
      ctx.fill()

      // 两侧缺口
      const clearY = holeTop
      this.clearArcFun(ctx, left, clearY, radius)
      this.clearArcFun(ctx, left + width, clearY, radius)
    },
    // 清除圆形区域
    clearArcFun(cxt, x, y, r) {
      let stepClear = 1 // 别忘记这一步
      clearArc(x, y, r)
      function clearArc(x, y, radius) {
        const calcWidth = radius - stepClear
        const calcHeight = Math.sqrt(radius * radius - calcWidth * calcWidth)

        const posX = x - calcWidth
        const posY = y - calcHeight

        const widthX = 2 * calcWidth
        const heightY = 2 * calcHeight

        if (stepClear <= radius) {
          cxt.clearRect(posX, posY, widthX, heightY)
          stepClear += 1
          clearArc(x, y, radius)
        }
      }
    },
    // 绘制图片
    async _drawImges(canvasContext, imgObj) {
      let temPath = ''
      if (imgObj.path) {
        temPath = await this._downloadImga(imgObj.path)
      }
      if (temPath === false) {
        return false
      }

      const info = await this._fileInfo(temPath)
      if (!imgObj.dHeight) {
        imgObj.dHeight = info.height
      } else {
        imgObj.dHeight = this.rpx2px(imgObj.dHeight) // 外部传入的是rpx
      }
      if (!imgObj.dWidth) {
        imgObj.dWidth = info.width
      } else {
        imgObj.dWidth = this.rpx2px(imgObj.dWidth) // 外部传入的是rpx
      }
      if (!imgObj.sHeight) {
        imgObj.sHeight = info.height
      } else {
        imgObj.sHeight = this.rpx2px(imgObj.sHeight) // 外部传入的是rpx
      }
      if (!imgObj.sWidth) {
        imgObj.sWidth = info.width
      } else {
        imgObj.sWidth = this.rpx2px(imgObj.sWidth) // 外部传入的是rpx
      }
      if (!imgObj.sx) {
        imgObj.sx = 0
      }
      if (!imgObj.sy) {
        imgObj.sy = 0
      }
      if (!imgObj.dx) {
        imgObj.dx = 0
      } else {
        imgObj.dx = this.rpx2px(imgObj.dx) // 外部传入的是rpx
      }
      if (!imgObj.dy) {
        imgObj.dy = 0
      } else {
        imgObj.dy = this.rpx2px(imgObj.dy) // 外部传入的是rpx
      }

      // 开始绘制图片
      if (!imgObj.radius) {
        canvasContext.drawImage(
          temPath,
          parseFloat(imgObj.sx),
          parseFloat(imgObj.sy),
          parseFloat(imgObj.sWidth),
          parseFloat(imgObj.sHeight),
          parseFloat(imgObj.dx),
          parseFloat(imgObj.dy),
          parseFloat(imgObj.dWidth),
          parseFloat(imgObj.dHeight),
        )
      } else {
        // 开始路径并移动到起点位置
        const x = imgObj.dx
        const y = imgObj.dy
        const width = imgObj.dWidth
        const height = imgObj.dHeight
        const radius = this.rpx2px(imgObj.radius)
        const ctx = canvasContext
        ctx.beginPath()
        ctx.moveTo(x + radius, y)

        // 从右上角开始绘制圆角
        ctx.lineTo(x + width - radius, y)
        ctx.arc(x + width - radius, y + radius, radius, -Math.PI / 2, 0) // 从右边绘制圆角

        // 从右下角开始绘制圆角
        ctx.lineTo(x + width, y + height - radius)
        ctx.arc(x + width - radius, y + height - radius, radius, 0, Math.PI / 2) // 从底部绘制圆角

        // 从左下角开始绘制圆角
        ctx.lineTo(x + radius, y + height)
        ctx.arc(x + radius, y + height - radius, radius, Math.PI / 2, Math.PI) // 从左边绘制圆角

        ctx.lineTo(x, y + radius)
        ctx.arc(x + radius, y + radius, radius, -Math.PI, -Math.PI / 2)
        // 最后连接起点与第一条直线
        ctx.closePath()
        // clip方法重要
        ctx.clip()
        ctx.drawImage(temPath, x, y, width, height)
        ctx.restore()
      }
    },
    // 绘制文字
    async _drawTxt(canvasContext, txtObj) {
      // console.log(txtObj);
      if (txtObj.color) {
        canvasContext.setFillStyle(txtObj.color)
      }
      if (!txtObj.size) {
        txtObj.size = 10
      } else {
        txtObj.size = this.rpx2px(txtObj.size)
      }
      txtObj.x = this.rpx2px(txtObj.x)
      txtObj.y = this.rpx2px(txtObj.y)
      canvasContext.setFontSize(parseFloat(txtObj.size))
      if (txtObj.align) {
        canvasContext.setTextAlign(txtObj.align)
      }
      if (txtObj.font) {
        canvasContext.font = txtObj.font
      }
      if (!txtObj.maxWidth) {
        txtObj.maxWidth = null
      }
      if (!txtObj.maxHeight) {
        txtObj.maxHeight = null
      }
      if (txtObj.backgroundColor) {
        let width = parseFloat(txtObj.size) * txtObj.value.length
        let height = parseFloat(txtObj.size)
        if (txtObj.maxWidth) {
          width = txtObj.maxWidth
        }
        if (txtObj.maxHeight) {
          height = txtObj.maxHeight
        }
        canvasContext.setFillStyle(txtObj.backgroundColor)
        canvasContext.fillRect(
          parseFloat(txtObj.x),
          parseFloat(txtObj.y) - parseFloat(txtObj.size),
          width,
          height,
        )
      }
      // console.log(txtObj);
      canvasContext.fillText(txtObj.value, parseFloat(txtObj.x), parseFloat(txtObj.y))
    },
    // 绘画二维码
    async _drowQr(canvasContext, qrObj) {
      qrObj.size = this.rpx2px(qrObj.size)
      qrObj.dx = this.rpx2px(qrObj.dx)
      qrObj.dy = this.rpx2px(qrObj.dy)

      const qr = new UQRCode()
      qr.setOptions({
        data: qrObj.url,
        size: qrObj.size,
      })

      qr.make()
      if (qrObj.backgroundColor) {
        canvasContext.setFillStyle(qrObj.backgroundColor)
        canvasContext.fillRect(
          qrObj.dx ? qrObj.dx : 0,
          qrObj.dy ? qrObj.dy : 0,
          qrObj.size,
          qrObj.size,
        )
      }
      const modules = qr.modules

      // console.log(qrObj.dx?qrObj.dx:0);
      for (let row = 0; row < modules.length; row++) {
        for (let col = 0; col < modules[row].length; col++) {
          const x = (qrObj.dx ? qrObj.dx : 0) + modules[row][col].x
          const y = (qrObj.dy ? qrObj.dy : 0) + modules[row][col].y
          const w = modules[row][col].width
          const h = modules[row][col].height
          let color = modules[row][col].color
          if (color === '#000000') {
            if (qrObj.color) {
              color = qrObj.color
            }
          }
          // let color = qrObj.color?qrObj.color:modules[row][col].color
          // console.log(color);
          canvasContext.setFillStyle(color)
          canvasContext.fillRect(x, y, w, h)
        }
      }
    },
    // 下载文件
    async _downloadImga(path) {
      // #ifndef H5
      try {
        const res = await uni.downloadFile({
          url: path,
          method: 'GET',
        })
        if (res.statusCode == 200) {
          return res.tempFilePath
        }
      } catch {
        return false
      }

      // #endif
      // #ifdef H5
      return path
      // #endif
      return false
    },
    // 获取文件详情
    async _fileInfo(temPath) {
      const res = await uni.getImageInfo({
        src: temPath,
      })
      if (res.errMsg === 'getImageInfo:ok') {
        return {
          width: res.width,
          height: res.height,
        }
      }
      return false
    },
    // 获取图片路径(有可能是base64)
    async getImgSrc() {
      const res = await uni.canvasToTempFilePath(
        {
          canvasId: this.canvasId,
        },
        this,
      )
      if (res.tempFilePath) {
        return res.tempFilePath
      }
      return false
    },
    async saveImg() {
      uni.canvasToTempFilePath(
        {
          canvasId: this.canvasId,
          success(res) {
            const tempFilePath = res.tempFilePath
            // #ifndef H5
            uni.saveImageToPhotosAlbum({
              filePath: tempFilePath,
              success(res) {
                uni.showToast({
                  title: '保存成功',
                  icon: 'none',
                })
              },
              fail(err) {
                uni.showToast({
                  title: '保存失败',
                  icon: 'none',
                })
                console.log('err', err)
              },
            })
            // #endif
            // #ifdef H5
            const arr = res.tempFilePath.split(',')
            const bytes = atob(arr[1])
            const ab = new ArrayBuffer(bytes.length)
            const ia = new Uint8Array(ab)
            for (let i = 0; i < bytes.length; i++) {
              ia[i] = bytes.charCodeAt(i)
            }
            const blob = new Blob([ab], {
              type: 'application/octet-stream',
            })
            const url = URL.createObjectURL(blob)
            const a = document.createElement('a')
            a.href = url
            a.download = new Date().valueOf() + '.png'
            const e = document.createEvent('MouseEvents')
            e.initMouseEvent(
              'click',
              true,
              false,
              window,
              0,
              0,
              0,
              0,
              0,
              false,
              false,
              false,
              false,
              0,
              null,
            )
            a.dispatchEvent(e)
            URL.revokeObjectURL(url)

            // #endif
          },
          fail(err) {
            uni.showToast({
              icon: 'none',
              title: '下载海报失败',
            })
            console.log('err', err)
          },
        },
        this,
      )
    },
    rpx2px(rpx) {
      return parseInt((this.screenWidth / 750) * rpx)
    },
  },
}
</script>
<style lang="scss">
.canvas-box {
  position: relative;
  top: 0;
  left: 0;
  overflow: hidden;
  .canvas-img {
    width: 100%;
    height: 100%;
  }
  .canvas-box-canvas {
    position: absolute;
    top: 0;
    left: -2000;
  }
}
</style>
